package MicroSpring.common;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;


public class PointCutUtils {


    public static PoinCutDefinition resolve(String value) {
        if (!StringUtils.hasText(value))
            return null;
        PoinCutDefinition poinCutDefinition = new PoinCutDefinition();
        if (value.startsWith("execution")){
            resolveByExecution(value,poinCutDefinition);
        }else if (value.startsWith("within")){
            resolveByWithin(value,poinCutDefinition);
        }else if (value.startsWith("@annotation")){
            resolveByAtAnno(value,poinCutDefinition);
        }else {
            //形如 point()
            resolveByNoop(value,poinCutDefinition);
        }
        return poinCutDefinition;
    }

    private static void resolveByNoop(String value, PoinCutDefinition poinCutDefinition) {
        poinCutDefinition.setTargetMethod(true);
        String methodN = value.split("\\(")[0];
        poinCutDefinition.setNamePattern(methodN);
        //目标方法为切点方法需要特殊处理
        poinCutDefinition.setTargetToPointCut(true);
        poinCutDefinition.setTargetPointCutMethodName(methodN);
        if (value.charAt(value.lastIndexOf("(")+1)==')')
            poinCutDefinition.setParamPattern("");
        else {
            String param = value.substring(value.lastIndexOf("(") + 1, value.lastIndexOf(")"));
            poinCutDefinition.setParamPattern(param);
        }
    }


    private static void resolveByAtAnno(String value, PoinCutDefinition poinCutDefinition) {
        poinCutDefinition.setTargetMethod(true);
        String s = value.split("@annotation\\(")[1];
        s=s.substring(0,s.length()-1);
        poinCutDefinition.setAnnoName(s);

    }

    private static void resolveByWithin(String value, PoinCutDefinition poinCutDefinition) {
        poinCutDefinition.setTargetClass(true);
        String str = value.split("within\\(")[1];
        str=str.substring(0,str.length()-1);
        if ("*".equals(str.split("\\.")[str.split("\\.").length-1])){
            poinCutDefinition.setPackageName(str.substring(0,str.lastIndexOf(".")));
        }else {
            poinCutDefinition.setDeclaringTypePattern(str);
        }
    }

    private static void resolveByExecution(String value, PoinCutDefinition poinCutDefinition) {
        poinCutDefinition.setTargetMethod(true);
        String str = value.split("execution\\(")[1];
        str=str.substring(0,str.length()-1);
       // System.out.println(s);//public * com.spring.service.BusinessObject.businessService(java.lang.String,..)
        String[] split = str.split(" ");
        int index=0;
        if (isModifiers(split[index])){
            poinCutDefinition.setModifiersPattern(split[index++]);
        }
        poinCutDefinition.setRetTypePpattern(split[index++]);
        if (isDeclarType(split[index])){
            String s1 = split[index].split("\\(")[0];
            String declarType=s1.substring(0,s1.lastIndexOf("."));
            String mName = s1.substring(s1.lastIndexOf(".")+1);
            String param = split[index].split("\\(")[1].substring(0,split[index].split("\\(")[1].lastIndexOf(")"));
            poinCutDefinition.setDeclaringTypePattern(declarType);
            poinCutDefinition.setNamePattern(mName);
            poinCutDefinition.setParamPattern(param);
            index++;
        }else {
            String mName = split[index].split("\\(")[0];
            String param = split[index].split("\\(")[1].substring(0, split[index].split("\\(")[1].lastIndexOf(")")-1);
            poinCutDefinition.setNamePattern(mName);
            poinCutDefinition.setParamPattern(param);
            index++;
        }
        if (index<split.length)
            poinCutDefinition.setThrowsPattern(split[index]);
    }

    private static boolean isDeclarType(String s) {
        if (s.split("\\(")[0].split("\\.").length<=1)
            return false;
        return true;
    }

    private static boolean isModifiers(String s) {
        return ("public".equals(s) || "protected".equals(s) || "private".equals(s));
    }





    public static Class[] getParamTypes(String paramPattern, ClassLoader classLoader) throws ClassNotFoundException {
        String[] split = paramPattern.split(",");
        if (split.length==0 || (split.length==1&&"".equals(split[0])))
            return null;
        Class []paramTypes=new Class[split.length];
        for (int i = 0; i < split.length; i++) {
            paramTypes[i]="..".equals(split[i]) ? Object[].class : classLoader.loadClass(split[i]);
        }
        return paramTypes;
    }

    public static Method[] getTargetMethodsByClass(Class<?> clazz, String namePattern, Class[] paramTypes) {
        if ("*".equals(namePattern))
            return clazz.getDeclaredMethods();
        List<Method>methodList=new ArrayList<>();
        for (Method declaredMethod : clazz.getDeclaredMethods()) {
            boolean f=true;
            if (declaredMethod.getName().equals(namePattern)){
                if (paramTypes==null && (declaredMethod.getParameterTypes() ==null || declaredMethod.getParameterTypes().length==0) ){
                    methodList.add(declaredMethod);
                    continue;
                }
                if (paramTypes[0] == Object[].class){
                    methodList.add(declaredMethod);
                    continue;
                }
                for (int i = 0; i < paramTypes.length; i++) {
                    if (declaredMethod.getParameterTypes()[i]!= paramTypes[i]){
                        f=false;
                        break;
                    }
                }
                if (f){
                    methodList.add(declaredMethod);
                }
            }
        }
        Method [] methods=new Method[methodList.size()];
        int idx=0;
        for (Method method : methodList) {
            methods[idx++]=method;
        }
        return methods;
    }
}
